package edu.cth.ai23;

import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferStrategy;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class Main extends Canvas {
    static final long serialVersionUID = 0;
    private BufferStrategy strategy;
    private boolean running;
    private IProblem problem;
    private int currentSolver = 0;
    private int currentProblem = 0;
    private IProblemSolver[] solvers = new IProblemSolver[3];
    private IProblem[] problems = new IProblem[2];

    private final static Font font = new Font("Times", Font.PLAIN, 14);


    private IProblemSolver solver;// = solvers[currentSolver];

    private Thread solverThread; //so that we can stop the previous solver

    public static void main(String[] args) {
        Main main = new Main();
        main.init();
        main.loop();
    }

    public void init() {
        //rc = new RubiksCube();
        problem = new RubiksCube();
        solvers[0] = new IDSSolver(problem);
        solvers[1] = new IDDFSSolver(problem);
        solvers[2] = new BreadthFirstSearch(problem);
        
        problems[0] = new RubiksCube();
        problems[1] = new EightPuzzle();
        solver = solvers[currentSolver];
    }

    public Main() {
        // create a frame to contain our game

        JFrame container = new JFrame("Rubiks Cube");

        // get hold the content of the frame and set up the resolution of the game

        JPanel panel = (JPanel) container.getContentPane();
        panel.setPreferredSize(new Dimension(800, 600));
        panel.setLayout(null);

        // setup our canvas size and put it into the content of the frame

        setBounds(0, 0, 800, 600);
        panel.add(this);

        // Tell AWT not to bother repainting our canvas since we're

        // going to do that our self in accelerated mode

        setIgnoreRepaint(true);

        // finally make the window visible

        container.pack();
        container.setResizable(false);
        container.setVisible(true);

        // add a listener to respond to the user closing the window. If they

        // do we'd like to exit the game

        container.addWindowListener(new WindowAdapter() {

            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }
        });

        // add a key input system (defined below) to our canvas

        // so we can respond to key pressed

        addKeyListener(new KeyInputHandler());

        // request the focus so key events come to us

        requestFocus();

        // create the buffering strategy which will allow AWT

        // to manage our accelerated graphics

        createBufferStrategy(2);
        strategy = getBufferStrategy();

        running = true;

        // initialise the entities in our game so there's something

        // to see at startup

    }

    public void loop() {
//        long lastLoopTime = System.currentTimeMillis();

        // keep looping round til the game ends

        while (running) {
            // work out how long its been since the last update, this

            // will be used to calculate how far the entities should

            // move this loop

//            long delta = System.currentTimeMillis() - lastLoopTime;
//            lastLoopTime = System.currentTimeMillis();

            // Get hold of a graphics context for the accelerated

            // surface and blank it out

            Graphics2D g = (Graphics2D) strategy.getDrawGraphics();
            g.setColor(Color.black);
            g.fillRect(0, 0, 800, 600);

            //rc.draw(g);
            g.setFont(font);
            problem.draw(g);

            g.setFont(font);
            if (this.solver != null)  {

                g.setColor(Color.gray);
                g.drawString("Solver: " + solver.getName(), 10, 20);
                g.setColor(Color.white);
                g.drawString("Solver: " + solver.getName(), 10+1, 20+1);

                g.setColor(Color.gray);
                g.drawString("Solver running time: " + solver.getRunningTime(), 10, 30);
                g.setColor(Color.white);
                g.drawString("Solver running time: " + solver.getRunningTime(), 10+1, 30+1);

                if (solver.isSolving()) {
                    g.setColor(Color.gray);
                    g.drawString("solving...", 10, 40);
                    g.setColor(Color.red);
                    g.drawString("solving...", 10+1, 40+1);
                } else {
                    g.setColor(Color.gray);
                    g.drawString("done!", 10, 40);
                    g.setColor(Color.green);
                    g.drawString("done!", 10+1, 40+1);

                    String report = solver.getReport();

                    String[] lines = report.split("\n");

                    int startYPos = 50;
                    int startXPos = 10;
                    for (String line : lines) {
                        g.setColor(Color.gray);
                        g.drawString(line, startXPos, startYPos);
                        g.setColor(Color.white);
                        g.drawString(line, startXPos+1, startYPos+1);
                        startYPos += 15;
                        if (startYPos > 590) {
                        	startYPos = 65;
                        	startXPos = startXPos + 100;
                        }

                    }
                }

            }

            // finally, we've completed drawing so clear up the graphics

            // and flip the buffer over

            g.dispose();
            strategy.show();

            try {
                Thread.sleep(10);
            } catch (Exception e) {
            }
        }
    }

    private class KeyInputHandler extends KeyAdapter {

        /** The number of key presses we've had while waiting for an "any key" press */
//        private int pressCount = 1;

        /**
         * Notification from AWT that a key has been pressed. Note that
         * a key being pressed is equal to being pushed down but *NOT*
         * released. Thats where keyTyped() comes in.
         *
         * @param e The details of the key that was pressed
         */
        public void keyPressed(KeyEvent e) {

            if (!solver.isSolving() || solver.isStoped()) {

                problem.handleKeyPress(e.getKeyCode());
            
                if (e.getKeyCode() == KeyEvent.VK_S) {
                    problem.scramble();
                }

                if (e.getKeyCode() == KeyEvent.VK_ENTER) {
                    problem.reset();
                }
                
                if (e.getKeyCode() == KeyEvent.VK_SPACE) {
                    // Create the thread supplying it with the runnable object

                    //solver.updateProblem(rc);
                    solver.updateProblem(problem);
                    solverThread = new Thread(solver);
                    // Start the thread
                    solverThread.start();
                    //rc.copyValues(solver.getRubiksCube());
                    problem.copyValues(solver.getProblem());

                }

                if (e.getKeyCode() == KeyEvent.VK_BACK_SPACE) {
                    currentSolver++;

                    if (currentSolver >= solvers.length)
                        currentSolver = 0;

                    solver = solvers[currentSolver];
                }

                if (e.getKeyCode() == KeyEvent.VK_P) {
                    currentProblem++;

                    if (currentProblem >= problems.length)
                        currentProblem = 0;

                    problem = problems[currentProblem];

                    solver.updateProblem(problem);
                }

            }

            if (e.getKeyCode() == KeyEvent.VK_N ) {
                solver.stop();
            }

            //System.out.println("Current quality: " + rc.evaluate());
        }

        public void keyReleased(KeyEvent e) {
            
        }

        public void keyTyped(KeyEvent e) {
            if (e.getKeyChar() == 27) {
                System.exit(0);
            }
        }
    }
}
